﻿using Anderson.NetLobby.Common;
using System.Net;
using System.Net.Sockets;
using System;
using System.Threading;
using System.ComponentModel;
using Anderson.NetLobby.MessageContent;
using System.Text;
using UnityEngine.Events;

namespace Anderson.NetLobby.Client
{
    public class NetLobbyClient
    {
        #region Properties
        private INetLobbyLog logger;

        private string m_serverHost;
        private int m_serverPort;
        private string m_userCode;

        private Socket clientSocket;
        NetLobbySession session;
        private BackgroundWorker bw = new BackgroundWorker();
        #endregion

        #region Status
        private bool m_connecting;
        private bool m_connected;
        private bool m_disconnecting;

        private bool m_confirming;
        private bool m_confirmd;

        private bool m_roomJoining;
        private bool m_roomJoined;

        private bool m_roomCreating;
        private bool m_roomCreated;
        private bool m_isRoomOwner;

        private long m_roomId;        
        private long m_playerId;
        #endregion

        #region Constructor
        public NetLobbyClient(string host, int port, string userCode, INetLobbyLog logger)
        {
            this.logger = logger;

            m_serverPort = port;
            m_serverHost = host;
            m_userCode = userCode;

            clientSocket = null;
            session = null;

            //status initing
            m_connecting = false;
            m_connected = false;
            m_disconnecting = false;

            m_confirming = false;
            m_confirmd = false;

            m_roomJoining = false;
            m_roomJoined = false;

            m_roomCreating = false;
            m_roomCreated = false;
            m_isRoomOwner = false;

            m_roomId = 0L;
            m_playerId = 0L;
        }
        #endregion

        #region Func
        public void Connect()
        {
            IPAddress ipAddress = IPAddress.Parse(m_serverHost);
            IPEndPoint serverEndPoint = new IPEndPoint(ipAddress, m_serverPort);

            m_connecting = true;
            clientSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            clientSocket.BeginConnect(serverEndPoint, OnConnectCallback, clientSocket);
        }        

        public void Disconnect()
        {
            if (m_connecting || !m_connected)
            {
                return;
            }

            try
            {
                StopReceive();

                clientSocket.Shutdown(SocketShutdown.Both);
                clientSocket.Close();                
            }
            catch (Exception e)
            {
                logger.LogError(e.Message);
            }            
        }

        public void CreateRoom()
        {

        }

        public void JoinRoom(long roomId)
        {

        }

        public void ExitRoom()
        {

        }

        public void StartRoom()
        {

        }

        public void CloseRoom()
        {

        }        
        #endregion

        #region Connect Callback
        private void OnConnectCallback(IAsyncResult ar)
        {
            try
            {
                m_connecting = false;

                Socket handler = (Socket)ar.AsyncState;
                handler.EndConnect(ar);
                logger.LogFormat("Connected to {0}", handler.RemoteEndPoint.ToString());

                m_connected = true;
                session = new NetLobbySession(handler, "", 0, DateTime.Now, logger);

                StartReceiveMessage();
            }
            catch (Exception e)
            {
                logger.LogError(e.Message);
            }
        }
        #endregion

        #region ReceiveMessage 
        
        private void StartReceiveMessage()
        {
            bw.WorkerReportsProgress = true;
            bw.WorkerSupportsCancellation = true;

            receiveDone.Set();

            bw.DoWork += OnDoWork;
            bw.ProgressChanged += OnProgressChanged;

            bw.RunWorkerAsync();
        }

        void StopReceive()
        {
            if (null != bw)
            {
                bw.CancelAsync();
            }
        }

        private void OnProgressChanged(object sender, ProgressChangedEventArgs e)
        {
        }

        private static ManualResetEvent receiveDone = new ManualResetEvent(false);
        private void OnDoWork(object sender, DoWorkEventArgs e)
        {
            BackgroundWorker worker = (BackgroundWorker)sender;

            while (true)
            {
                if (worker.CancellationPending)
                {
                    logger.Log("- CancellationPending");
                    e.Cancel = true;
                    break;
                }

                try
                {                    
                    clientSocket.BeginReceive(session.buffer, 0, NetLobbySession.BufferSize, SocketFlags.None, new AsyncCallback(ReadCallback), session);

                    receiveDone.WaitOne();
                }
                catch (Exception exp)
                {
                    Console.WriteLine(exp.ToString());
                }

                //worker.ReportProgress(0, state);
            }
        }

        private void ReadCallback(IAsyncResult ar)
        {
            //logger.Log("ReadCallback");

            NetLobbySession session = (NetLobbySession)ar.AsyncState;
            Socket handler = session.playerSocket;

            bool socketDisconnectOrError = false;
            try
            {
                int bytesRead = handler.EndReceive(ar);

                if (bytesRead > 0)
                {
                    byte[] readBytes = new byte[bytesRead];
                    Buffer.BlockCopy(session.buffer, 0, readBytes, 0, bytesRead);

                    byte[] fullMessageBytes = session.ReceiveMessage(readBytes);
                    if (null != fullMessageBytes)
                    {
                        AnalyzeNetMessage(fullMessageBytes);
                    }
                }
                else
                {
                    socketDisconnectOrError = true;
                }
            }
            catch (SocketException e)
            {
                logger.LogError(e.Message);

                if (e.SocketErrorCode == SocketError.HostDown)
                {
                    socketDisconnectOrError = true;
                }
            }

            if (socketDisconnectOrError)
            {
                session.ClientSideClose();
            }
            else
            {
                //handler.BeginReceive(session.buffer, 0, NetLobbySession.BufferSize, SocketFlags.None, new AsyncCallback(ReadCallback), session);
            }

            receiveDone.Set();
        }

        #endregion

        #region 收到的消息分析
        private void AnalyzeNetMessage(byte[] fullMessageBytes)
        {
            //TODO; Comment;
            StringBuilder s = new StringBuilder();
            for (int i = 0; i < fullMessageBytes.Length; i++)
            {
                s.Append(fullMessageBytes[i]);
            }
            logger.Log(s.ToString());

            bool error = false;
            //反序列化
            try
            {
                int sizeOfUInt32 = sizeof(uint);
                int sizeOfLong = sizeof(long);
                int size = sizeOfUInt32 * 6 + sizeOfLong;
                int count = fullMessageBytes.Length - size;

                ByteStream byteStream = new ByteStream(fullMessageBytes);

                uint length = byteStream.ReadUInt32();
                uint bof = byteStream.ReadUInt32();
                uint l1 = byteStream.ReadUInt32();
                uint l2 = byteStream.ReadUInt32();
                uint l3 = byteStream.ReadUInt32();
                long roomId = byteStream.ReadInt64();
                byte[] content = byteStream.ReadBytes(count);
                uint eof = byteStream.ReadUInt32();

                if (NetLobbyDefination.NET_MESSAGE_BOF != bof)
                {
                    logger.LogError("AnalyzeNetMessage BOF Error.");
                    error = true;
                }
                else if (NetLobbyDefination.NET_MESSAGE_EOF != eof)
                {
                    logger.LogError("AnalyzeNetMessage EOF Error.");
                    error = true;
                }
                else if (null == content || content.Length == 0)
                {
                    logger.LogError("AnalyzeNetMessage Content Length Error.");
                    error = true;
                }

                if (!error)
                {
                    ReceiveNetMessage(fullMessageBytes, roomId, l1, l2, l3);
                }
            }
            catch (Exception e)
            {
                logger.LogError(e.Message);
            }
        }

        private void ReceiveNetMessage(byte[] fullMessageBytes, long roomId, uint l1, uint l2, uint l3)
        {
            uint v = l1 & MessageLevelDefinition.MASK_CLIENT_L1_BIZ;
            if (v == MessageLevelDefinition.CLIENT_L1_BIZ)
            {
                ReceiveNetMessage_Business(fullMessageBytes, roomId, l1, l2, l3);

                return;
            }

            switch (l1)
            {
                case MessageLevelDefinition.SERVER_L1_SYSTEM:
                    ReceiveNetMessage_System(fullMessageBytes, roomId, l1, l2, l3);
                    break;
                case MessageLevelDefinition.CLIENT_L1_ROOM:
                    ReceiveNetMessage_Room(fullMessageBytes, roomId, l1, l2, l3);
                    break;
                default:
                    logger.LogError("ReceiveNetMessage : Unkonwn L1 Type");
                    break;
            }
        }

        private void ReceiveNetMessage_Business(byte[] fullMessageBytes, long roomId, uint l1, uint l2, uint l3)
        {
            //TODO; 主要业务逻辑全在这
        }

        private void ReceiveNetMessage_System(byte[] fullMessageBytes, long roomId, uint l1, uint l2, uint l3)
        {
            if (l2 == MessageLevelDefinition.SERVER_L2_SYSTEM_PLAYERID)
            {
                if (l3 == MessageLevelDefinition.SERVER_L3_SYSTEM_PLAYERID_CMD)
                {
                    PlayerIdClientConfirm(fullMessageBytes);
                    return;
                }
            }

            if (l2 == MessageLevelDefinition.SERVER_L2_SYSTEM_HEART)
            {
                if (l3 == MessageLevelDefinition.SERVER_L3_SYSTEM_HEART)
                {
                    NetMessage<MC_Heart> netMessage = MessageSerialize.Deserialize<MC_Heart>(fullMessageBytes);
                    MC_Heart content = netMessage.MessageContent;

                    bw.ReportProgress(1, content.frameCount);

                    return;
                }
            }
        }

        private void PlayerIdClientConfirm(byte[] fullMessageBytes)
        {
            logger.Log("Server.PlayerIdClientConfirm");

            //DONE; Send ConfirmId
            NetMessage<MC_PlayerId> netMessage = MessageSerialize.Deserialize<MC_PlayerId>(fullMessageBytes);
            MC_PlayerId setPlayerId = netMessage.MessageContent;
            setPlayerId.idFromClient = setPlayerId.idFromServer;
            setPlayerId.codeFromClient = m_userCode;

            NetMessage<MC_PlayerId> netMessageSend = new NetMessage<MC_PlayerId>(
                0,
                MessageLevelDefinition.CLIENT_L1_ROOM,
                MessageLevelDefinition.CLIENT_L2_ROOM_CREATE,
                MessageLevelDefinition.SERVER_L3_ROOM_CREATE_RES,
                setPlayerId);

            byte[] bytes = MessageSerialize.Serialize(netMessageSend);
            session.playerSocket.BeginSend(bytes, 0, bytes.Length, SocketFlags.None, new AsyncCallback(SendCallBack), session);
        }

        private void ReceiveNetMessage_Room(byte[] fullMessageBytes, long roomId, uint l1, uint l2, uint l3)
        {
            if (l2 == MessageLevelDefinition.CLIENT_L2_ROOM_CREATE)
            {
                if (l3 == MessageLevelDefinition.SERVER_L3_ROOM_CREATE_RES)
                {
                    CreateRoomResonse(fullMessageBytes);
                    return;
                }
            }

            if (l2 == MessageLevelDefinition.CLIENT_L2_ROOM_LIST)
            {
                if (l3 == MessageLevelDefinition.SERVER_L3_ROOM_LIST_GET_RES || l3 == MessageLevelDefinition.SERVER_L3_ROOM_LIST_CHANGED)
                {
                    GetRoomListResonse(fullMessageBytes);
                    return;
                }
            }

            if (l2 == MessageLevelDefinition.CLIENT_L2_ROOM_ACTION)
            {
                if (l3 == MessageLevelDefinition.SERVER_L3_ROOM_ENTER_RES)
                {
                    CloseRoomResonse(fullMessageBytes);
                    return;
                }

                if (l3 == MessageLevelDefinition.SERVER_L3_ROOM_START_RES)
                {
                    CloseRoomResonse(fullMessageBytes);
                    return;
                }

                if (l3 == MessageLevelDefinition.SERVER_L3_ROOM_CLOSE_RES)
                {
                    CloseRoomResonse(fullMessageBytes);
                    return;
                }
            }
        }

        private void CloseRoomResonse(byte[] fullMessageBytes)
        {
            //TODO;
        }

        private void GetRoomListResonse(byte[] fullMessageBytes)
        {
            //TODO;
        }

        private void CreateRoomResonse(byte[] fullMessageBytes)
        {
            //TODO;
        }
        #endregion

        private void SendCallBack(IAsyncResult ar)
        {
            try
            {
                NetLobbySession session = (NetLobbySession)ar.AsyncState;
                Socket handler = session.playerSocket;

                handler.EndSend(ar);
            }
            catch (Exception e)
            {
                logger.LogError(e.Message);
            }
        }
    }

}
